/*     */ package com.zimbra.cs.redolog;
/*     */ 
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.redolog.logger.FileHeader;
/*     */ import com.zimbra.cs.redolog.logger.FileLogReader;
/*     */ import com.zimbra.cs.redolog.logger.LogWriter;
/*     */ import com.zimbra.cs.redolog.op.AbortTxn;
/*     */ import com.zimbra.cs.redolog.op.Checkpoint;
/*     */ import com.zimbra.cs.redolog.op.CommitTxn;
/*     */ import com.zimbra.cs.redolog.op.RedoableOp;
/*     */ import com.zimbra.cs.redolog.op.StoreIncomingBlob;
/*     */ import java.io.File;
/*     */ import java.io.IOException;
/*     */ import java.util.ArrayList;
/*     */ import java.util.HashMap;
/*     */ import java.util.HashSet;
/*     */ import java.util.Iterator;
/*     */ import java.util.LinkedHashMap;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ import java.util.Map.Entry;
/*     */ import java.util.Set;
/*     */ import java.util.concurrent.ConcurrentHashMap;
/*     */ import java.util.concurrent.ConcurrentMap;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class RedoPlayer
/*     */ {
/*     */   private static final int INITIAL_MAP_SIZE = 1000;
/*  53 */   private final Object mOpsMapGuard = new Object();
/*     */   
/*     */   private LinkedHashMap<TransactionId, RedoableOp> mOpsMap;
/*     */   
/*     */   private boolean mWritable;
/*     */   
/*     */   private boolean mUnloggedReplay;
/*     */   private boolean mIgnoreReplayErrors;
/*     */   private boolean mSkipDeleteOps;
/*     */   protected boolean handleMailboxConflict;
/*  63 */   protected ConcurrentMap<Integer, Integer> mailboxConflicts = new ConcurrentHashMap();
/*     */   
/*     */   public RedoPlayer(boolean writable) {
/*  66 */     this(writable, false, false, false, false);
/*     */   }
/*     */   
/*     */   public RedoPlayer(boolean writable, boolean unloggedReplay, boolean ignoreReplayErrors, boolean skipDeleteOps, boolean handleMailboxConflict)
/*     */   {
/*  71 */     this.mOpsMap = new LinkedHashMap(1000);
/*  72 */     this.mWritable = writable;
/*  73 */     this.mUnloggedReplay = unloggedReplay;
/*  74 */     this.mIgnoreReplayErrors = ignoreReplayErrors;
/*  75 */     this.mSkipDeleteOps = skipDeleteOps;
/*  76 */     this.handleMailboxConflict = handleMailboxConflict;
/*     */   }
/*     */   
/*     */   public void shutdown() {
/*  80 */     this.mOpsMap.clear();
/*     */   }
/*     */   
/*     */   public void scanLog(File logfile, boolean redoCommitted, Map<Integer, Integer> mboxIDsMap, long startTime, long endTime)
/*     */     throws IOException, ServiceException
/*     */   {
/*  86 */     scanLog(logfile, redoCommitted, mboxIDsMap, startTime, endTime, Long.MAX_VALUE);
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   private void scanLog(File logfile, boolean redoCommitted, Map<Integer, Integer> mboxIDsMap, long startTime, long endTime, long ignoreCommitsAtOrAfter)
/*     */     throws IOException, ServiceException
/*     */   {
/* 117 */     FileLogReader logReader = new FileLogReader(logfile, this.mWritable);
/* 118 */     logReader.open();
/* 119 */     long lastPosition = 0L;
/*     */     
/*     */     try
/*     */     {
/* 123 */       RedoableOp op = null;
/* 124 */       while ((op = logReader.getNextOp()) != null) {
/* 125 */         lastPosition = logReader.position();
/*     */         
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 134 */         if (ZimbraLog.redolog.isDebugEnabled()) {
/* 135 */           ZimbraLog.redolog.debug("Read: " + op);
/*     */         }
/* 137 */         processOp(op, redoCommitted, mboxIDsMap, startTime, endTime, ignoreCommitsAtOrAfter);
/*     */ 
/*     */       }
/*     */       
/*     */ 
/*     */     }
/*     */     catch (IOException e)
/*     */     {
/*     */ 
/* 146 */       ZimbraLog.redolog.warn("IOException while reading redolog file", e);
/*     */       
/* 148 */       long size = logReader.getSize();
/* 149 */       if (lastPosition < size) {
/* 150 */         long diff = size - lastPosition;
/* 151 */         String msg = "There were " + diff + " bytes of junk data at the end of " + logfile.getAbsolutePath() + ".";
/*     */         
/*     */ 
/*     */ 
/*     */ 
/* 156 */         if (this.mWritable) {
/* 157 */           ZimbraLog.redolog.warn(msg + "  File will be truncated to " + lastPosition + " bytes.");
/*     */           
/* 159 */           logReader.truncate(lastPosition);
/*     */         } else {
/* 161 */           ZimbraLog.redolog.warn(msg);
/*     */         }
/*     */       }
/* 164 */     } finally { logReader.close();
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/* 170 */   private boolean mHasOrphanOps = false;
/* 171 */   private Map<TransactionId, RedoableOp> mOrphanOps = new HashMap();
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   private final void processOp(RedoableOp op, boolean redoCommitted, Map<Integer, Integer> mboxIDsMap, long startTime, long endTime, long ignoreCommitsAtOrAfter)
/*     */     throws ServiceException
/*     */   {
/* 182 */     if (op.isStartMarker()) {
/* 183 */       synchronized (this.mOpsMapGuard) {
/* 184 */         this.mOpsMap.put(op.getTransactionId(), op);
/* 185 */         if (this.mHasOrphanOps) {
/* 186 */           RedoableOp x = (RedoableOp)this.mOrphanOps.remove(op.getTransactionId());
/* 187 */           if (x != null) {
/* 188 */             ZimbraLog.redolog.error("Detected out-of-order insertion of change record for orphans commit/abort: change=" + op + ", orphan=" + x);
/*     */           }
/*     */           
/*     */         }
/*     */         
/*     */       }
/*     */     }
/* 195 */     else if ((op instanceof Checkpoint)) {
/* 196 */       Checkpoint ckpt = (Checkpoint)op;
/* 197 */       Set txns = ckpt.getActiveTxns();
/* 198 */       if (txns.size() > 0) {
/* 199 */         synchronized (this.mOpsMapGuard) {
/* 200 */           if (this.mOpsMap.size() != txns.size())
/*     */           {
/* 202 */             if (ZimbraLog.redolog.isDebugEnabled()) {
/* 203 */               StringBuffer sb1 = new StringBuffer("Current Uncommitted Ops: ");
/* 204 */               StringBuffer sb2 = new StringBuffer("Checkpoint Uncommitted Ops: ");
/* 205 */               int i = 0;
/* 206 */               for (Iterator it = this.mOpsMap.keySet().iterator(); it.hasNext(); i++) {
/* 207 */                 TransactionId id = (TransactionId)it.next();
/* 208 */                 if (i > 0)
/* 209 */                   sb1.append(", ");
/* 210 */                 sb1.append(id);
/*     */               }
/* 212 */               i = 0;
/* 213 */               for (Iterator it = txns.iterator(); it.hasNext(); i++) {
/* 214 */                 TransactionId id = (TransactionId)it.next();
/* 215 */                 if (i > 0)
/* 216 */                   sb2.append(", ");
/* 217 */                 sb2.append(id);
/*     */               }
/* 219 */               ZimbraLog.redolog.info("Checkpoint discrepancy: # current uncommitted ops = " + this.mOpsMap.size() + ", # checkpoint uncommitted ops = " + txns.size() + "\nMAP DUMP:\n" + sb1.toString() + "\n" + sb2.toString());
/*     */             }
/*     */             
/*     */           }
/*     */           
/*     */         }
/*     */       } else {
/* 226 */         synchronized (this.mOpsMapGuard) {
/* 227 */           if (this.mOpsMap.size() != 0)
/*     */           {
/* 229 */             if (ZimbraLog.redolog.isDebugEnabled()) {
/* 230 */               StringBuffer sb1 = new StringBuffer("Current Uncommitted Ops: ");
/* 231 */               int i = 0;
/* 232 */               for (Iterator it = this.mOpsMap.keySet().iterator(); it.hasNext(); i++) {
/* 233 */                 TransactionId id = (TransactionId)it.next();
/* 234 */                 if (i > 0)
/* 235 */                   sb1.append(", ");
/* 236 */                 sb1.append(id);
/*     */               }
/* 238 */               ZimbraLog.redolog.info("Checkpoint discrepancy: # current uncommitted ops = " + this.mOpsMap.size() + " instead of 0\nMAP DUMP:\n" + sb1.toString());
/*     */             }
/*     */             
/*     */           }
/*     */         }
/*     */       }
/*     */     }
/* 245 */     else if (op.isEndMarker())
/*     */     {
/*     */ 
/* 248 */       boolean isCommitOp = op instanceof CommitTxn;
/* 249 */       long opTstamp = op.getTimestamp();
/* 250 */       if ((!isCommitOp) || (opTstamp < ignoreCommitsAtOrAfter))
/*     */       {
/*     */         RedoableOp prepareOp;
/*     */         
/* 254 */         synchronized (this.mOpsMapGuard) {
/* 255 */           prepareOp = (RedoableOp)this.mOpsMap.remove(op.getTransactionId());
/* 256 */           if (prepareOp == null) {
/* 257 */             this.mHasOrphanOps = true;
/* 258 */             ZimbraLog.redolog.error("Commit/abort record encountered before corresponding change record (" + op + ")");
/* 259 */             TransactionId tid = op.getTransactionId();
/* 260 */             RedoableOp x = (RedoableOp)this.mOrphanOps.get(tid);
/* 261 */             if (x != null)
/* 262 */               ZimbraLog.redolog.error("Op [" + op + "] is already in orphans map: value=" + x);
/* 263 */             this.mOrphanOps.put(tid, op);
/*     */           }
/*     */         }
/*     */         
/* 267 */         if ((redoCommitted) && (prepareOp != null) && (isCommitOp) && ((startTime == -1L) || (prepareOp.getTimestamp() >= startTime)) && (opTstamp < endTime))
/*     */         {
/*     */ 
/* 270 */           boolean allowRedo = false;
/* 271 */           int opMailboxId; if (mboxIDsMap == null)
/*     */           {
/* 273 */             allowRedo = true;
/*     */           } else {
/* 275 */             opMailboxId = prepareOp.getMailboxId();
/* 276 */             if ((prepareOp instanceof StoreIncomingBlob)) {
/* 277 */               assert (opMailboxId == -1);
/*     */               
/*     */ 
/* 280 */               StoreIncomingBlob storeOp = (StoreIncomingBlob)prepareOp;
/* 281 */               List<Integer> list = storeOp.getMailboxIdList();
/* 282 */               Object opMboxIds; if (list != null) {
/* 283 */                 opMboxIds = new HashSet(list);
/* 284 */                 for (Object entry : mboxIDsMap.entrySet()) {
/* 285 */                   if (((Set)opMboxIds).contains(((Map.Entry)entry).getKey())) {
/* 286 */                     allowRedo = true;
/*     */                     
/*     */ 
/*     */ 
/* 290 */                     List<Integer> newList = new ArrayList(mboxIDsMap.values());
/*     */                     
/* 292 */                     storeOp.setMailboxIdList(newList);
/* 293 */                     break;
/*     */                   }
/*     */                   
/*     */                 }
/*     */                 
/*     */               }
/*     */               else
/*     */               {
/* 301 */                 allowRedo = true;
/*     */               }
/* 303 */             } else if (opMailboxId == -1)
/*     */             {
/*     */ 
/*     */ 
/* 307 */               allowRedo = true;
/*     */             } else {
/* 309 */               for (Map.Entry<Integer, Integer> entry : mboxIDsMap.entrySet()) {
/* 310 */                 if (opMailboxId == ((Integer)entry.getKey()).intValue()) {
/* 311 */                   if (entry.getValue() != null)
/*     */                   {
/* 313 */                     prepareOp.setMailboxId(((Integer)entry.getValue()).intValue());
/*     */                   }
/* 315 */                   allowRedo = true;
/* 316 */                   break;
/*     */                 }
/*     */               }
/*     */             }
/*     */           }
/* 321 */           if (allowRedo) {
/* 322 */             if ((this.mSkipDeleteOps) && (prepareOp.isDeleteOp())) {
/* 323 */               ZimbraLog.redolog.info("Skipping delete op: " + prepareOp.toString());
/*     */             } else {
/*     */               try {
/* 326 */                 if (ZimbraLog.redolog.isDebugEnabled())
/* 327 */                   ZimbraLog.redolog.debug("Redoing: " + prepareOp.toString());
/* 328 */                 prepareOp.setUnloggedReplay(this.mUnloggedReplay);
/* 329 */                 playOp(prepareOp);
/*     */               } catch (Exception e) {
/* 331 */                 if (!ignoreReplayErrors()) {
/* 332 */                   throw ServiceException.FAILURE("Error executing redoOp", e);
/*     */                 }
/* 334 */                 ZimbraLog.redolog.warn("Ignoring error during redo log replay: " + e.getMessage(), e);
/*     */               }
/*     */             }
/*     */           }
/*     */         }
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   protected boolean ignoreReplayErrors()
/*     */   {
/* 345 */     return this.mIgnoreReplayErrors;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   protected void playOp(RedoableOp op)
/*     */     throws Exception
/*     */   {
/* 353 */     if (this.handleMailboxConflict) {
/* 354 */       redoOpWithMboxConflict(op);
/*     */     } else {
/* 356 */       op.redo();
/*     */     }
/*     */   }
/*     */   
/*     */   protected void redoOpWithMboxConflict(RedoableOp op) throws Exception {
/*     */     try {
/* 362 */       Integer newId = (Integer)this.mailboxConflicts.get(Integer.valueOf(op.getMailboxId()));
/*     */       
/* 364 */       if (newId != null) {
/* 365 */         ZimbraLog.redolog.warn("mailbox conflict, mapping old ID %d to %d", new Object[] { Integer.valueOf(op.getMailboxId()), newId });
/* 366 */         op.setMailboxId(newId.intValue());
/*     */       }
/* 368 */       op.redo();
/*     */     } catch (MailboxIdConflictException mice) {
/* 370 */       ZimbraLog.redolog.warn("found mismatched mailboxId %d expected %d", new Object[] { Integer.valueOf(mice.getFoundId()), Integer.valueOf(mice.getExpectedId()) });
/* 371 */       this.mailboxConflicts.put(Integer.valueOf(mice.getExpectedId()), Integer.valueOf(mice.getFoundId()));
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   public int runCrashRecovery(RedoLogManager redoLogMgr, List<RedoableOp> postStartupRecoveryOps)
/*     */     throws Exception
/*     */   {
/* 387 */     File redoLog = redoLogMgr.getLogFile();
/* 388 */     if (!redoLog.exists()) {
/* 389 */       return 0;
/*     */     }
/* 391 */     long lookBackTstamp = Long.MAX_VALUE;
/* 392 */     long lookBackDuration = RedoConfig.redoLogCrashRecoveryLookbackSec() * 1000L;
/* 393 */     if (lookBackDuration > 0L)
/*     */     {
/*     */ 
/*     */ 
/*     */ 
/* 398 */       long logLastModTime = redoLog.lastModified();
/* 399 */       long logCreateTime = new FileLogReader(redoLog).getHeader().getCreateTime();
/* 400 */       long lastOpTstamp = Math.max(logLastModTime, logCreateTime);
/* 401 */       lookBackTstamp = lastOpTstamp - lookBackDuration;
/*     */     }
/*     */     
/*     */ 
/*     */ 
/*     */ 
/* 407 */     LogWriter logWriter = redoLogMgr.getLogWriter();
/* 408 */     logWriter.close();
/* 409 */     scanLog(redoLog, false, null, Long.MIN_VALUE, Long.MAX_VALUE, lookBackTstamp);
/* 410 */     logWriter.open();
/*     */     
/*     */     int numOps;
/* 413 */     synchronized (this.mOpsMapGuard) {
/* 414 */       numOps = this.mOpsMap.size();
/*     */     }
/* 416 */     if (numOps == 0) {
/* 417 */       ZimbraLog.redolog.info("No uncommitted transactions to redo");
/* 418 */       return 0;
/*     */     }
/*     */     
/* 421 */     synchronized (this.mOpsMapGuard) {
/* 422 */       Set entrySet = this.mOpsMap.entrySet();
/* 423 */       ZimbraLog.redolog.info("Redoing " + numOps + " uncommitted transactions");
/* 424 */       for (Iterator it = entrySet.iterator(); it.hasNext();) {
/* 425 */         Map.Entry entry = (Map.Entry)it.next();
/* 426 */         RedoableOp op = (RedoableOp)entry.getValue();
/* 427 */         if (op != null)
/*     */         {
/*     */ 
/* 430 */           if (op.deferCrashRecovery()) {
/* 431 */             ZimbraLog.redolog.info("Deferring crash recovery to after startup: " + op);
/* 432 */             postStartupRecoveryOps.add(op);
/*     */           }
/*     */           else
/*     */           {
/* 436 */             if (ZimbraLog.redolog.isInfoEnabled()) {
/* 437 */               ZimbraLog.redolog.info("REDOING: " + op);
/*     */             }
/* 439 */             boolean success = false;
/*     */             try {
/* 441 */               op.redo();
/* 442 */               success = true; } catch (Exception e) { CommitTxn commit;
/*     */               AbortTxn abort;
/* 444 */               ZimbraLog.redolog.error("Redo failed for [" + op + "]." + "  Backend state of affected item is indeterminate." + "  Marking operation as aborted and moving on.", e);
/*     */             } finally {
/*     */               CommitTxn commit;
/*     */               AbortTxn abort;
/* 448 */               if (success) {
/* 449 */                 CommitTxn commit = new CommitTxn(op);
/* 450 */                 redoLogMgr.logOnly(commit, true);
/*     */               } else {
/* 452 */                 AbortTxn abort = new AbortTxn(op);
/* 453 */                 redoLogMgr.logOnly(abort, true);
/*     */               }
/*     */             }
/*     */           } } }
/* 457 */       this.mOpsMap.clear();
/*     */     }
/*     */     
/* 460 */     return numOps;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   protected LinkedHashMap<TransactionId, RedoableOp> getCopyOfUncommittedOpsMap()
/*     */   {
/*     */     LinkedHashMap<TransactionId, RedoableOp> map;
/*     */     
/* 469 */     synchronized (this.mOpsMapGuard) { LinkedHashMap<TransactionId, RedoableOp> map;
/* 470 */       if (this.mOpsMap != null) {
/* 471 */         map = new LinkedHashMap(this.mOpsMap);
/*     */       } else
/* 473 */         map = new LinkedHashMap();
/*     */     }
/* 475 */     return map;
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/redolog/RedoPlayer.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */